package com.droidblackbox.manager;

import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import com.droidblackbox.preference.PreferManager;

public class WASManager {

	private static WASManager instance = null;
	private InputStream inputStream = null;
	private StringBuffer url = null;
	private StringBuffer parameters = null;
	private String host;
	private int port = -1;
	private String page;

	private Map<String, String> map = null;

	public static final String EMAIL;
	public static final String KEY_BODY;
	public static final String KEY_HEADER;
	public static final String KEY_MESSAGE;
	public static final String KEY_NUMBER;
	public static final String KEY_PUSH;
	public static final String KEY_RECEIVE;
	public static final String KEY_REGISTRATION;
	public static final String KEY_FILES;

	public static final String HEADER_PARTNER;
	public static final String HEADER_PHONE;
	public static final String HEADER_ONE_FILE;

	public static final String BODY_INSERT;
	public static final String BODY_SELECT;
	public static final String BODY_UPDATE;
	public static final String BODY_DELETE;

	public static final String RECEIVE_ON;
	public static final String RECEIVE_OFF;

	public static final String APP_NAME;
	public static final String WAS_NAME;

	public static final String BOUNDARY;

	static {
		KEY_BODY = ParameterKey.body.toString();
		KEY_HEADER = ParameterKey.header.toString();
		KEY_MESSAGE = ParameterKey.message.toString();
		KEY_NUMBER = ParameterKey.number.toString();
		KEY_PUSH = ParameterKey.push.toString();
		KEY_RECEIVE = ParameterKey.receive.toString();
		KEY_REGISTRATION = ParameterKey.registration.toString();
		KEY_FILES = ParameterKey.files.toString();

		HEADER_PARTNER = HeaderValues.partner.toString();
		HEADER_PHONE = HeaderValues.phone.toString();
		HEADER_ONE_FILE = HeaderValues.oneFile.toString();

		BODY_INSERT = BodyValues.insert.toString();
		BODY_SELECT = BodyValues.select.toString();
		BODY_UPDATE = BodyValues.update.toString();
		BODY_DELETE = BodyValues.delete.toString();

		RECEIVE_ON = "1";
		RECEIVE_OFF = "0";

		EMAIL = "zeros0317@gmail.com";
		APP_NAME = "DroidBlackBox";
		WAS_NAME = "DroidBlackBoxWAS";

		BOUNDARY = "DroidBlackBox";
	}

	private enum ParameterKey {
		header, body, number, registration, receive, message, push, files
	};

	private enum HeaderValues {
		partner, phone, oneFile
	};

	private enum BodyValues {
		insert, select, update, delete
	};

	public synchronized static WASManager getInstance() {
		if (instance == null) {
			synchronized (WASManager.class) {
				if (instance == null) {
					instance = new WASManager();
				}
			}
		}

		return instance;
	}

	private WASManager() {
	}

	public void setUrl(StringBuffer url) {
		this.url = url;
	}

	public void setUrl(String url) {
		if (this.url != null)
			this.url = null;

		this.url = new StringBuffer();
		this.url.append(url);

	}

	/**
	 * @return the host
	 */
	public String getHost() {
		return host;
	}

	/**
	 * @param host
	 *            the host to set
	 */
	public void setHost(String host) {
		this.host = host;
	}

	/**
	 * @return the port
	 */
	public int getPort() {
		return port;
	}

	/**
	 * @param port
	 *            the port to set
	 */
	public void setPort(int port) {
		this.port = port;
	}

	/**
	 * @return the page
	 */
	public String getPage() {
		return page;
	}

	/**
	 * @param page
	 *            the page to set
	 */
	public void setPage(String page) {
		this.page = page;
	}

	public static void add(String key, String value) {
		if (getInstance().map == null)
			getInstance().map = new HashMap<String, String>();

		getInstance().map.put(key, value);

	}

	public void clear() {
		if (map != null)
			map.clear();
		map = null;
		parameters = null;

	}

	private boolean preConn() {
		boolean b = true;
		final String host = PreferManager
				.get(PreferManager.KEY_STRING_SERVER_ADDRESS);
		final int port = getInstance().getPort();
		final String page = getInstance().getPage();

		if (host == null || page == null)
			b = false;

		if (port < 0)
			b = false;

		return b;
	}

	private String getHostURL() {
		final StringBuffer strURL = new StringBuffer();
		strURL.append("http://");
		strURL.append(host);
		strURL.append(":");
		strURL.append(port);
		strURL.append("/");
		strURL.append(WASManager.WAS_NAME);
		strURL.append("/");
		strURL.append(page);

		return strURL.toString();
	}

	public static void conn(final FileInputStream fis, final String FILENAME) {
		if (getInstance().preConn() == false)
			return;

		Thread thread = new Thread(new Runnable() {
			public void run() {
				try {
					URL url = new URL(getInstance().getHostURL());

					HttpURLConnection conn = (HttpURLConnection) url
							.openConnection();
					conn.setDoOutput(true);
					conn.setUseCaches(false);
					conn.setRequestMethod("POST");
					conn.setRequestProperty("Connection", "Keep-Alive");
					conn.setRequestProperty("Content-Type",
							"multipart/form-data;boundary=" + BOUNDARY);

					String newLine = "\r\n";
					String hyphen = "--";

					StringBuffer stringBuffer = new StringBuffer();
					stringBuffer.append(newLine);
					stringBuffer.append(hyphen);
					stringBuffer.append(BOUNDARY);
					stringBuffer.append(newLine);
					stringBuffer.append("Content-Disposition: ");
					stringBuffer.append("form-data; name=");
					stringBuffer.append("\"");
					stringBuffer.append(KEY_HEADER);
					stringBuffer.append("\"");
					stringBuffer.append(newLine);
					stringBuffer.append(newLine);
					stringBuffer.append(HEADER_ONE_FILE);
					stringBuffer.append(newLine);
					stringBuffer.append(hyphen);
					stringBuffer.append(BOUNDARY);
					stringBuffer.append(newLine);
					stringBuffer.append("Content-Disposition: ");
					stringBuffer.append("form-data; name=");
					stringBuffer.append("\"");
					stringBuffer.append(KEY_NUMBER);
					stringBuffer.append("\"");
					stringBuffer.append(newLine);
					stringBuffer.append(newLine);
					stringBuffer.append(C2DMManager.getInstance().getNumber());
					stringBuffer.append(newLine);
					stringBuffer.append(hyphen);
					stringBuffer.append(BOUNDARY);
					stringBuffer.append(newLine);
					stringBuffer.append("Content-Disposition: ");
					stringBuffer.append("form-data; name=");
					stringBuffer.append("\"");
					stringBuffer.append(KEY_FILES);
					stringBuffer.append("\"");
					stringBuffer.append(";filename=");
					stringBuffer.append("\"");
					stringBuffer.append(FILENAME);
					stringBuffer.append("\"");
					stringBuffer.append(newLine);
					stringBuffer.append("Content-Type: ");
					stringBuffer.append("application/octet-stream");
					stringBuffer.append(newLine);
					stringBuffer.append(newLine);

					// Output스트림을 열어
					DataOutputStream dos = new DataOutputStream(conn
							.getOutputStream());
					dos.writeBytes(stringBuffer.toString());

					// 버퍼사이즈를 설정하여 buffer할당
					int bytesAvailable = fis.available();
					int maxBufferSize = 1024;
					int bufferSize = Math.min(bytesAvailable, maxBufferSize);
					byte[] buffer = new byte[bufferSize];

					// 스트림에 작성
					int bytesRead = fis.read(buffer, 0, bufferSize);
					while (bytesRead > 0) {
						// Upload file part(s)
						dos.write(buffer, 0, bufferSize);
						bytesAvailable = fis.available();
						bufferSize = Math.min(bytesAvailable, maxBufferSize);
						bytesRead = fis.read(buffer, 0, bufferSize);
					}
					dos.writeBytes(newLine);
					dos.writeBytes(hyphen + BOUNDARY + hyphen + newLine);
					fis.close();

					// 써진 버퍼를 stream에 출력.
					dos.flush();
					// Get the response

					getInstance().inputStream = conn.getInputStream();
				} catch (FileNotFoundException e) {
					getInstance().inputStream = null;
					e.printStackTrace();
				} catch (SocketTimeoutException e) {
					getInstance().inputStream = null;
					e.printStackTrace();
				} catch (Exception e) {
					e.printStackTrace();
				} finally {
					getInstance().clear();
				}

			}
		});
		thread.setDaemon(true);
		thread.start();
	}

	public static void conn() {
		if (getInstance().preConn() == false)
			return;

		Thread thread = new Thread(new Runnable() {

			public void run() {
				try {

					Iterator<String> iterator = getInstance().map.keySet()
							.iterator();
					while (iterator.hasNext()) {
						final String sKey = (String) iterator.next();
						final String sValue = getInstance().map.get(sKey);
						final String key = getInstance().getChangeEncoder(sKey);
						final String value = getInstance().getChangeEncoder(
								sValue);

						if (getInstance().parameters == null) {
							getInstance().parameters = new StringBuffer();
							getInstance().parameters.append(key);
							getInstance().parameters.append("=");
							getInstance().parameters.append(value);
						} else {
							getInstance().parameters.append("&");
							getInstance().parameters.append(key);
							getInstance().parameters.append("=");
							getInstance().parameters.append(value);
						}

					}
					byte[] postData = getInstance().parameters.toString()
							.getBytes("UTF8");

					URL url = new URL(getInstance().getHostURL());

					HttpURLConnection conn = (HttpURLConnection) url
							.openConnection();
					conn.setDoOutput(true);
					conn.setUseCaches(false);
					conn.setRequestMethod("POST");
					conn.setRequestProperty("Content-Type",
							"application/x-www-form-urlencoded");
					conn.setRequestProperty("Content-Length",
							Integer.toString(postData.length));

					OutputStream out = conn.getOutputStream();
					out.write(postData);
					out.flush();
					out.close();

					// Get the response

					getInstance().inputStream = conn.getInputStream();
				} catch (FileNotFoundException e) {
					getInstance().inputStream = null;
					e.printStackTrace();
				} catch (SocketTimeoutException e) {
					getInstance().inputStream = null;
					e.printStackTrace();
				} catch (Exception e) {
					e.printStackTrace();
				} finally {
					getInstance().clear();
				}

			}
		});
		thread.setDaemon(true);
		thread.start();

	}

	public static void conn(String url) {
		getInstance().setUrl(url);
		conn();
	}

	public static void conn(StringBuffer parameters) {
		getInstance().parameters = parameters;
		conn();
	}

	public static void conn(StringBuffer url, StringBuffer parameters) {
		getInstance().setUrl(url);
		getInstance().parameters = parameters;
		conn();
	}

	public static void conn(String url, StringBuffer parameters) {
		getInstance().setUrl(url);
		getInstance().parameters = parameters;
		conn();
	}

	private String getChangeEncoder(String key) throws Exception {
		return URLEncoder.encode(key, "UTF-8");
	}

	public InputStream result() {
		return this.inputStream;
	}
}
